/**
 *    Copyright 2006-2017 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.mybatis.generator.plugins;

import org.mybatis.generator.api.FullyQualifiedTable;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.PluginAdapter;
import org.mybatis.generator.api.dom.java.*;
import org.mybatis.generator.api.dom.xml.Attribute;
import org.mybatis.generator.api.dom.xml.Document;
import org.mybatis.generator.api.dom.xml.TextElement;
import org.mybatis.generator.api.dom.xml.XmlElement;

import java.util.*;

import static org.mybatis.generator.internal.util.StringUtility.stringHasValue;

/**
 * 分页实现类
 */
public class PaginationPlugin extends PluginAdapter {

    private String importTypes;//自定义引入的类

    private static final String import_split = ";";//引入类的分隔符

    private Map<FullyQualifiedTable, List<XmlElement>> elementsToAdd;
    //防止重复创建方法
    private Map<String, String> javaMethod = new HashMap<String, String>();
    private Map<String, String> xmlMethod = new HashMap<String, String>();

    //init
    public PaginationPlugin() {
        this.elementsToAdd = new HashMap<FullyQualifiedTable, List<XmlElement>>();
    }

    //set example
    @Override
    public boolean modelExampleClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        if (stringHasValue(importTypes)) {
            String[] split = importTypes.split(import_split);
            for (String type : split) {
                topLevelClass.addImportedType(new FullyQualifiedJavaType(type));
            }
        } else {
            //default page query type, need change
            topLevelClass.addImportedType("org.mybatis.generator.plugins.custome.PageQuery");
        }
        topLevelClass.setSuperClass("PageQuery");
        return super.modelExampleClassGenerated(topLevelClass, introspectedTable);
    }

    //add method to mapper
    @Override
    public boolean clientSelectByExampleWithBLOBsMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
        if (javaMethod.get(introspectedTable.getAliasedFullyQualifiedTableNameAtRuntime()) == null) {
            interfaze.addMethod(addSelectPageMethod(method, introspectedTable));
            javaMethod.put(introspectedTable.getAliasedFullyQualifiedTableNameAtRuntime(), "ok");
        }
        return true;
    }


    @Override
    public boolean clientSelectByExampleWithoutBLOBsMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
        if (javaMethod.get(introspectedTable.getAliasedFullyQualifiedTableNameAtRuntime()) == null) {
            interfaze.addMethod(addSelectPageMethod(method, introspectedTable));
            javaMethod.put(introspectedTable.getAliasedFullyQualifiedTableNameAtRuntime(), "ok");
        }
        return true;
    }

    /**
     * 新增select方法
     *
     * @param method
     * @param introspectedTable
     * @return
     */
    private Method addSelectPageMethod(Method method, IntrospectedTable introspectedTable) {
        Method newMethod = new Method();
        newMethod.setReturnType(method.getReturnType());
        newMethod.setName("selectPage");//set method name
        newMethod.setVisibility(method.getVisibility());
        for (Parameter parameter : method.getParameters()) {
            newMethod.addParameter(parameter);
        }
        newMethod.addJavaDocLine("//分页查询");
        return newMethod;
    }

    @Override
    public boolean sqlMapSelectByExampleWithoutBLOBsElementGenerated(
            XmlElement element, IntrospectedTable introspectedTable) {
        if (checkGeneratedXml(element, introspectedTable)) return true;
        return true;
    }

    @Override
    public boolean sqlMapSelectByExampleWithBLOBsElementGenerated(
            XmlElement element, IntrospectedTable introspectedTable) {
        if (checkGeneratedXml(element, introspectedTable)) return true;
        return true;
    }

    /**
     * 检查是否已经生成好了xml方法
     *
     * @param element
     * @param introspectedTable
     * @return
     */
    private boolean checkGeneratedXml(XmlElement element, IntrospectedTable introspectedTable) {
        if (xmlMethod.get(introspectedTable.getFullyQualifiedTableNameAtRuntime()) == null) {
            if (introspectedTable.getTargetRuntime() == IntrospectedTable.TargetRuntime.MYBATIS3) {
                copyAndSaveElement(element, introspectedTable.getFullyQualifiedTable());
            }
            xmlMethod.put(introspectedTable.getFullyQualifiedTableNameAtRuntime(), "ok");
            return true;
        }
        return false;
    }

    /**
     * We'll override this method and add any new elements generated by
     * previous calls
     */
    @Override
    public boolean sqlMapDocumentGenerated(Document document,
                                           IntrospectedTable introspectedTable) {
        List<XmlElement> elements = elementsToAdd.get(introspectedTable.getFullyQualifiedTable());
        if (elements != null) {
            for (XmlElement element : elements) {
                document.getRootElement().addElement(element);
            }
        }
        return true;
    }

    /**
     * Use the method copy constructor to create a new element
     *
     * @param element
     * @param fqt
     */
    private void copyAndSaveElement(XmlElement element, FullyQualifiedTable fqt) {
        XmlElement newElement = new XmlElement(element);

        // remove old id attribute and add a new one with the new name
        for (Iterator<Attribute> iterator = newElement.getAttributes().iterator(); iterator.hasNext(); ) {
            Attribute attribute = iterator.next();
            if ("id".equals(attribute.getName())) { //$NON-NLS-1$
                iterator.remove();
                Attribute newAttribute = new Attribute("id", "selectPage"); //$NON-NLS-1$ //$NON-NLS-2$
                newElement.addAttribute(newAttribute);
                break;
            }
        }

        // save the new element locally.   We'll add it to the document
        // later
        List<XmlElement> elements = elementsToAdd.get(fqt);
        if (elements == null) {
            elements = new ArrayList<XmlElement>();
            elementsToAdd.put(fqt, elements);
        }
        newElement.addElement(new TextElement("  LIMIT #{startPos},#{pageSize}"));
        elements.add(newElement);
    }


    /**
     * This plugin is always valid - no properties are required
     */
    public boolean validate(List<String> warnings) {
        importTypes = properties.getProperty("importTypes");
        return true;
    }
}
